package de.unikoblenz.west.csxpoi;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

/**
 * A buffer for the categories from the ontology.
 */
public class OntologyBuffer {

	/**
	 * the list of categories
	 */
	private static List<CategoryWrapper> mCategories = new ArrayList<CategoryWrapper>();

	/**
	 * Gets the buffered categories.
	 * 
	 * @return a copy of the list of categories
	 */
	public static List<CategoryWrapper> getCategories() {
		return new ArrayList<CategoryWrapper>(mCategories);
	}

	/**
	 * Adds a category to the list of buffered categories.
	 * 
	 */
	public static void addCategory(CategoryWrapper myCategory) {
		mCategories.add(myCategory);
	}

	/**
	 * Gets the buffered categories excluding the specified one.
	 * 
	 * @param exception
	 *            the category to exclude
	 * @return a copy of the list of categories excluding the specified one
	 */
	public static List<CategoryWrapper> getCategories(CategoryWrapper exception) {
		List<CategoryWrapper> categories = getCategories();
		categories.remove(exception);
		return categories;
	}

	/**
	 * Gets the specified buffered category
	 * 
	 * @param exception
	 *            the category to exclude
	 * @return a copy of the list of categories excluding the specified one
	 */
	public static CategoryWrapper getNamedCategory(CharSequence name) {
		List<CategoryWrapper> categories = getCategories();

		for (int i = 0; i < categories.size(); i++) {
			if (categories.get(i).getName() == name)
				return categories.get(i);
		}

		CategoryWrapper myCategory = CategoryWrapper.createFromName(name.toString());
		return myCategory;
	}

	/**
	 * Gets the URI of a category with the specified name
	 * 
	 * @param name
	 *            the name of the category whose URI to retrieve
	 * @return the URI of a category with the specified name or null if there is
	 *         no such category in the list of categories
	 */
	public static String getUriForCategoryName(String name) {
		for (CategoryWrapper category : mCategories) {
			if (category.getName().equals(name)) {
				return category.getUri();
			}
		}
		return null;
	}

	/**
	 * Updates the list of categories.
	 * 
	 * @return true if the update was successful
	 */
	public static boolean updateCategories() {
		boolean success = false;
		try {
			URL url = new URL(Constants.REST_ADDRESS + "categories");
			SAXParserFactory spf = SAXParserFactory.newInstance();
			SAXParser sp = spf.newSAXParser();
			XMLReader xr = sp.getXMLReader();
			CategoriesSaxHandler categoriesSaxHandler = new CategoriesSaxHandler();
			xr.setContentHandler(categoriesSaxHandler);
			xr.parse(new InputSource(url.openStream()));
			List<CategoryWrapper> list = categoriesSaxHandler.getList();
			if (list.size() > 0) {
				mCategories = list;
				success = true;
			}
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return success;
	}

	/**
	 * Parses the XML response from the categories REST request.
	 */
	private static class CategoriesSaxHandler extends DefaultHandler {

		/**
		 * the list of categories
		 */
		private List<CategoryWrapper> mList = new ArrayList<CategoryWrapper>();

		/**
		 * the current category
		 */
		private CategoryWrapper mCurrentCategory = null;

		/**
		 * the characters of the current tag
		 */
		private String mCurrentCharacters = "";

		/**
		 * the constant for the name tag
		 */
		private static final String NAME_TAG = "name";

		/**
		 * the constant for the category tag
		 */
		private static final String CATEGORY_TAG = "category";

		/**
		 * the constant for the id tag
		 */
		private static final String ID_ATTRIBUTE = "id";

		/**
		 * Handles opening tags.
		 */
		@Override
		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			if (localName.equals(CATEGORY_TAG)) {
				mCurrentCategory = new CategoryWrapper();
				mCurrentCategory.setUri(attributes.getValue(ID_ATTRIBUTE));
			}
		}

		/**
		 * Handles closing tags.
		 */
		@Override
		public void endElement(String uri, String localName, String qName)
				throws SAXException {
			if (mCurrentCategory != null) {
				if (localName.equals(NAME_TAG)) {
					mCurrentCategory.setName(mCurrentCharacters);
				} else if (localName.equals(CATEGORY_TAG)) {
					mList.add(mCurrentCategory);
					mCurrentCategory = null;
				}
			}
		}

		/**
		 * Handles characters of tags.
		 */
		@Override
		public void characters(char[] ch, int start, int length)
				throws SAXException {
			mCurrentCharacters = new String(ch, start, length);
		}

		/**
		 * Gets the list of categories.
		 * 
		 * @return the list of categories
		 */
		public List<CategoryWrapper> getList() {
			return mList;
		}
	}

}
